Skip to content

fix(persona): one predicate for what a context is, and colour that says which way a copy went - #192

Merged
stormer78 merged 1 commit into
mainfrom
feat/persona-map-colour
Sep 8, 2026
Merged

fix(persona): one predicate for what a context is, and colour that says which way a copy went#192
stormer78 merged 1 commit into
mainfrom
feat/persona-map-colour

Conversation

@stormer78

@stormer78 stormer78 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Rebased onto main now that #191 has merged, so this is one commit and speaks attribute throughout.

Two things the live console was getting wrong on the persona pane.

A context was denied and drawn at the same time

The map answered "is this holder known here?" with three different tests:

where test said
header pill a persona wears a face known in 1 of 12
contexts band a persona record exists 2 cards
fold row whatever the band left not known in 10

1 + 10 ≠ 12, and the context that fell through the crack was drawn under a band captioned where you are known while the header denied it.

The state underneath is real, and it had no words anywhere on screen. persona/binding/list/1.0 enumerates the personas present in a context and carries bound separately, so unbinding a face leaves the persona behind: that context still knows an identifier of the holder's and can address it, while holding none of their attributes. Calling that known overstates what left; folding it in with they hold nothing about you hides an identifier the holder has out there.

standingOf / tallyContexts (identity-graph.ts) now give one predicate with four answers — known, identified, unreadable, absent — and the header, the band and the fold all read it. A test pins that the four sum to the total. The card keeps its place under its own heading:

An identifier only — glenn-vta · wears nothing
This context can address that identifier. It holds nothing else of yours.

Colour, in three channels that never overlap

Border = selection and reach. Stripe and chip dots = classification. Pills = status.

  • Direction. reachOf has always run asymmetrically — down from an attribute to where copies of it went, up from a context to what it holds — and one accent hid the asymmetry it was computing. Down borrows --m-act-data from the contexts band it ends in; up keeps the accent; a face selection splits visibly across the dashed line, which is the model the pane exists to teach. Flow is computed in identity-graph.ts with the rest of the model — the component still only draws. The key appears with the first selection and not before.
  • Family. Attribute cards group under their claim-type family (Who you are, How to reach you, Where you already appear, Your agent asks first, Not in the registry), with the hue repeated as a dot on every face's type chips, so a composition reads without opening it. attribute-family.ts classifies only roots the vendored registry declares — profile.* and employer land in Not in the registry, because inventing a "profile" family here would draw a grouping nobody has agreed to, in a colour that reads as though someone had checked.
  • The palette rule holds. --m-fam-* are categorical, the same species as the act colours; --w-ok / --w-warn / --w-danger remain the only colours that mean anything. A test asserts no family's words claim the colour protects something: the stripe defends nothing, exactly as the mask beside it defends only a screen.
  • Unlit "wear" edges went neutral — teal used to mean "a face is worn here" at rest and "this is the path you selected" when lit.

Checks

  • 19 new tests. Extension 328 pass, core 548 pass.
  • tsc -b clean across workspaces; npm run build clean, manager.js still a single chunk and dist/background.js unchanged.
  • Reach direction, the four standings and their sum, the family rules and the no-overclaim rule are all pinned; the three new render tests mount the real pane.

Not in this PR

Base automatically changed from docs/persona-attribute-vocabulary to main September 8, 2026 10:25
…ys which way a copy went

The identity map answered "is this holder known here?" three times with three
different tests. The header counted contexts where a persona *wears a face*, the
band drew a card for every context holding a persona *record*, and the fold
counted whatever the band left over — so a live console read "known in 1 of 12"
above two cards with ten folded away. One of the three numbers was lying to
whoever read it, and there was no way to tell which.

The state that fell between them is real. `persona/binding/list/1.0` enumerates
the personas *present* in a context and carries `bound` separately, so unbinding
a face leaves the persona behind: that context still knows an identifier of the
holder's and can address it, while holding none of their attributes. "Known"
overstates what left; "absent" hides an identifier they have out there. It now
has a standing of its own and words on screen — *An identifier only*, and "This
context can address that identifier. It holds nothing else of yours."

- `standingOf` / `tallyContexts` in `identity-graph.ts`: known, identified,
  unreadable, absent, each context counted once. The header, the band and the
  fold all read this one predicate, and a test pins that the four sum to the
  total — the property the old arithmetic broke.

The second half is colour, in three channels that never overlap: the border is
selection and reach, the stripe and chip dots are classification, the pills are
status.

- Reach is painted in two hues rather than one. `reachOf` has always run
  asymmetrically — down from an attribute to where copies of it went, up from a
  context to what it holds — and a single accent hid the asymmetry it computed.
  Down borrows `--m-act-data` from the contexts band it ends in, up keeps the
  accent, and a face selection splits visibly across the dashed line. `Flow` is
  computed in `identity-graph.ts` with the rest of the model; the component
  still only draws. The key appears with the first selection and not before.
- Attributes group under their claim-type family, with the family's hue
  repeated as a dot on every face's type chips, so a composition reads without
  opening it. `attribute-family.ts` classifies **only** roots the vendored
  registry declares: `profile.*` and `employer` resolve to *Not in the
  registry*, because inventing a "profile" family locally would draw a grouping
  nobody has agreed to in a colour that reads as though someone had checked.
- The `--m-fam-*` tokens are categorical, the same species as the act colours,
  and `manager-theme.css`'s rule stands: `--w-ok` / `--w-warn` / `--w-danger`
  remain the only colours that mean anything. A test asserts no family's words
  claim the colour protects something — the stripe defends nothing, exactly as
  the mask beside it defends only a screen.
- Unlit "wear" edges went neutral. Teal used to mean "a face is worn here" at
  rest and "this is the path you selected" when lit; one hue, two jobs.

19 new tests (328 extension, 548 core), `tsc -b` clean, `npm run build` clean
with `manager.js` still a single chunk.

Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
@stormer78
stormer78 force-pushed the feat/persona-map-colour branch from 54a969d to 7f58c4f Compare September 8, 2026 10:27
@stormer78
stormer78 merged commit 3c9ad09 into main Sep 8, 2026
3 checks passed
@stormer78
stormer78 deleted the feat/persona-map-colour branch September 8, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant